<?php

class DboMysql extends DboSource {

/**
 * Description property.
 *
 * @var string
 */
	public $description = "MySQL DBO Base Driver";

	public $_baseConfig = array(
		'persistent' => true,
		'host' => 'localhost',
		'login' => 'root',
		'password' => '',
		'database' => 'psp',
		'port' => '3306'
	);	
	
/**
 * Start quote
 *
 * @var string
 */
	public $startQuote = "`";

/**
 * End quote
 *
 * @var string
 */
	public $endQuote = "`";

/**
 * Index of basic SQL commands
 *
 * @var array
 * @access protected
 */
	public $_commands = array(
		'begin'    => 'START TRANSACTION',
		'commit'   => 'COMMIT',
		'rollback' => 'ROLLBACK'
	);	
	//用来设置自己定义要显示的字段名的属性
	public $fieldParameters = array(
		'charset' => array('value' => 'CHARACTER SET', 'quote' => false, 'join' => ' ', 'column' => false, 'position' => 'beforeDefault'),
		'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => ' ', 'column' => 'Collation', 'position' => 'beforeDefault'),
		'comment' => array('value' => 'COMMENT', 'quote' => true, 'join' => ' ', 'column' => 'Comment', 'position' => 'afterDefault')
	);
/**
 * MySQL column definition
 *
 * @var array
 */
	public $columns = array(
		'primary_key' => array('name' => 'NOT NULL AUTO_INCREMENT'),
		'string' => array('name' => 'varchar', 'limit' => '255'),
		'text' => array('name' => 'text'),
		'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'),
		'float' => array('name' => 'float', 'formatter' => 'floatval'),
		'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
		'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
		'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
		'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
		'binary' => array('name' => 'blob'),
		'boolean' => array('name' => 'tinyint', 'limit' => '1')
	);	
	
/**
 * Connects to the database using options in the given configuration array.
 *
 * @return boolean True if the database could be connected, else false
 */
	public function connect() {
		$config = $this->config;
		$this->connected = false;

		if (!$config['persistent']) {
			$this->connection = mysql_connect($config['host'] . ':' . $config['port'], $config['login'], $config['password'], true);
			$config['connect'] = 'mysql_connect';
		} else {
			$this->connection = mysql_pconnect($config['host'] . ':' . $config['port'], $config['login'], $config['password']);
		}

		if (mysql_select_db($config['database'], $this->connection)) {
			$this->connected = true;
		}

		if (!empty($config['encoding'])) {
			$this->setEncoding($config['encoding']);
		}

		$this->_useAlias = (bool)version_compare(mysql_get_server_info($this->connection), "4.1", ">=");
		return $this->connected;
	}

/**
 * Check whether the MySQL extension is installed/loaded
 *
 * @return boolean
 */
	public function enabled() {
		return extension_loaded('mysql');
	}
/**
 * Disconnects from database.
 *
 * @return boolean True if the database could be disconnected, else false
 */
	public function disconnect() {
		if (isset($this->results) && is_resource($this->results)) {
			mysql_free_result($this->results);
		}
		$this->connected = !@mysql_close($this->connection);
		return !$this->connected;
	}	

/**
 * Executes given SQL statement.
 *
 * @param string $sql SQL statement
 * @return resource Result resource identifier
 * @access protected
 */
	public function _execute($sql) {
  	//echo '{'.$sql.'}<br/>';  
		return mysql_query($sql, $this->connection);
	}
    function QueryAll($sql,$choose='') {   
	    $array = array();
        $result = $this->_execute($sql);           
        while ($row = $this->FetchArray($result)){
		    if(!empty($choose)){				  
				$key = $row[$choose];
				$array[$key] = $row;				  
			}else{
				$array[] = $row;				
			}			    
        }		
		if (isset($result) && is_resource($result)) {
			mysql_free_result($result);
		}		
        return $array;
       
    }	
	function FetchArray($query, $result_type = MYSQL_ASSOC){
		return mysql_fetch_array($query, $result_type);
	}	
	
	function TableDescribe($tablename)
	{
		$fields = array();
		$result = $this->_execute("SHOW COLUMNS FROM $tablename");
		while($r = $this->FetchArray($result)) {
			$fields[] = $r['Field'];
		}
		if (isset($result) && is_resource($result)) {
			mysql_free_result($result);
		}
		return $fields;
	}		
/**
 * Returns an array of sources (tables) in the database.
 *
 * @return array Array of tablenames in the database
 */
	public function listSources() {
		$cache = parent::listSources();
		if ($cache != null) {
			return $cache;
		}
		$result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database']) . ';');
		if (!$result) {
			return array();
		} else {
			$tables = array();
			while ($line = mysql_fetch_row($result)) {
				$tables[] = $line[0];
			}
			parent::listSources($tables);
			return $tables;
		}
	}

	function describe(&$model) {
		$cache = parent::describe($model);
		if ($cache != null) {
			return $cache;
		}
		$fields = false;
		$cols = $this->fetchAll('SHOW FULL COLUMNS FROM ' . $this->fullTableName($model));
		foreach ($cols as $column) {
			$colKey = array_keys($column);
			if (isset($column[$colKey[0]]) && !isset($column[0])) {
				$column[0] = $column[$colKey[0]];
			}
			if (isset($column[0])) {
				$fields[$column[0]['Field']] = array(
					'type' => $this->column($column[0]['Type']),
					'null' => ($column[0]['Null'] == 'YES' ? true : false),
					'default' => $column[0]['Default'],
					'length' => $this->length($column[0]['Type']),
				);
				if (!empty($column[0]['Key']) && isset($this->index[$column[0]['Key']])) {
					$fields[$column[0]['Field']]['key'] = $this->index[$column[0]['Key']];
				}
				foreach ($this->fieldParameters as $name => $value) {//用来设置自己定义要显示的字段名的属性
					if (!empty($column[0][$value['column']])) {
						$fields[$column[0]['Field']][$name] = $column[0][$value['column']];
					}
				}
				if (isset($fields[$column[0]['Field']]['collate'])) {
					$charset = $this->getCharsetName($fields[$column[0]['Field']]['collate']);
					if ($charset) {
						$fields[$column[0]['Field']]['charset'] = $charset;
					}
				}
			}
		}
		$this->__cacheDescription($this->fullTableName($model, false), $fields);
		return $fields;
	}	
	

/**
 * Converts database-layer column types to basic types
 *
 * @param string $real Real database-layer column type (i.e. "varchar(255)")
 * @return string Abstract column type (i.e. "string")
 */
	function column($real) {
		if (is_array($real)) {
			$col = $real['name'];
			if (isset($real['limit'])) {
				$col .= '('.$real['limit'].')';
			}
			return $col;
		}

		$col = str_replace(')', '', $real);
		$limit = $this->length($real);
		if (strpos($col, '(') !== false) {
			list($col, $vals) = explode('(', $col);
		}

		if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
			return $col;
		}
		if (($col == 'tinyint' && $limit == 1) || $col == 'boolean') {
			return 'boolean';
		}
		if (strpos($col, 'int') !== false) {
			return 'integer';
		}
		if (strpos($col, 'char') !== false || $col == 'tinytext') {
			return 'string';
		}
		if (strpos($col, 'text') !== false) {
			return 'text';
		}
		if (strpos($col, 'blob') !== false || $col == 'binary') {
			return 'binary';
		}
		if (strpos($col, 'float') !== false || strpos($col, 'double') !== false || strpos($col, 'decimal') !== false) {
			return 'float';
		}
		if (strpos($col, 'enum') !== false) {
			return "enum($vals)";
		}
		return 'text';
	}	
	
/**

 mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。
下列字符受影响：\x00  \n  \r  \  '  "  \x1a
如果成功，则该函数返回被转义的字符串。如果失败，则返回 false。
 
 */
	public function value($data, $column = null, $safe = false) {
		$parent = parent::value($data, $column, $safe);
		if ($parent != null) {
			return $parent;
		}
		if ($data === null || (is_array($data) && empty($data))) {
			return 'NULL';
		}
		if ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
			return  "''";
		}
		if (empty($column)) {
			$column = $this->introspectType($data);
		}

		switch ($column) {
			case 'boolean':
				return $this->boolean((bool)$data);
			break;
			case 'integer':
			case 'float':
				if ($data === '') {
					return 'NULL';
				}
				if (is_float($data)) {
					return sprintf('%F', $data);
				}
				if ((is_int($data) || $data === '0') || (
					is_numeric($data) && strpos($data, ',') === false &&
					$data[0] != '0' && strpos($data, 'e') === false)
				) {
					return $data;
				}
			default:

				return "'" . mysql_real_escape_string($data, $this->connection) . "'"; 
			break;
		}
	}

/**
 * Returns a formatted error message from previous database operation.
 *
 * @return string Error message with error number
 */
	public function lastError() {
		if (mysql_errno($this->connection)) {
			return mysql_errno($this->connection).': '.mysql_error($this->connection);
		}
		return null;
	}

/**
mysql_affected_rows() 函数返回前一次 MySQL 操作所影响的记录行数
取得最近一次与 connection 关联的 INSERT，UPDATE 或 DELETE 查询所影响的记录行数。
返回值
执行成功，则返回受影响的行的数目，如果最近一次查询失败的话，函数返回 -1。
如果最近一次操作是没有任何条件（WHERE）的 DELETE 查询，在表中所有的记录都会被删除，但本函数返回值在 4.1.2 版之前都为 0。
当使用 UPDATE 查询，MySQL 不会将原值与新值一样的列更新。这样使得 mysql_affected_rows() 函数返回值不一定就是查询条件所符合的记录数，
只有真正被修改的记录数才会被返回。
REPLACE 语句首先删除具有相同主键的记录，然后插入一个新记录。该函数返回的是被删除的记录数加上被插入的记录数。
 */
	public function lastAffected() {
		if ($this->_result) {
			return mysql_affected_rows($this->connection);
		}
		return null;
	}

/**
mysql_num_rows() 返回结果集中行的数目。
此命令仅对 SELECT 语句有效。要取得被 INSERT，UPDATE 或者 DELETE 查询所影响到的行的数目，用 mysql_affected_rows()。
 */
	public function lastNumRows() {
		if ($this->hasResult()) {
			return mysql_num_rows($this->_result);
		}
		return null;
	}

/**
的到最后插入id号
 */
	public function lastInsertId($source = null) {
		$id = $this->fetchRow('SELECT LAST_INSERT_ID() AS insertID', false);
		if ($id !== false && !empty($id) && !empty($id[0]) && isset($id[0]['insertID'])) {
			return $id[0]['insertID'];
		}
		return null;
	}

/**
---------
mysql_num_fields() 函数返回执行 mysql_query() 函数产生的结果集中字段的数,如果失败，则返回 false。
mysql_fetch_field() 函数从结果集中取得列信息并作为对象返回。
mysql_fetch_field() 可以用来从查询结果中取得字段的信息。如果没有指定字段偏移量，则提取下一个尚未被 mysql_fetch_field() 取得的字段。
该函数返回一个包含字段信息的对象。
被返回的对象的属性为：
    name - 列名
    table - 该列所在的表名
    max_length - 该列最大长度
    not_null - 1，如果该列不能为 NULL
    primary_key - 1，如果该列是 primary key
    unique_key - 1，如果该列是 unique key
    multiple_key - 1，如果该列是 non-unique key
    numeric - 1，如果该列是 numeric
    blob - 1，如果该列是 BLOB
    type - 该列的类型
    unsigned - 1，如果该列是无符号数
    zerofill - 1，如果该列是 zero-filled
--------------
得到返回结果集中每个字的表明和字段名的组合数组
 */
	public function resultSet(&$results) {
		if (isset($this->results) && is_resource($this->results) && $this->results != $results) {
			mysql_free_result($this->results);
		}
		$this->results =& $results;
		$this->map = array();
		$numFields = mysql_num_fields($results);
		$index = 0;
		$j = 0;
		while ($j < $numFields) {
			$column = mysql_fetch_field($results, $j);
			if (!empty($column->table)) {
				$this->map[$index++] = array($column->table, $column->name);
			} else {
				$this->map[$index++] = array(0, $column->name);
			}
			$j++;
		}
	}

/**
mysql_fetch_row() 函数从结果集中取得一行作为数字数组。
mysql_fetch_row() 从和结果标识 data 关联的结果集中取得一行数据并作为数组返回。每个结果的列储存在一个数组的单元中，偏移量从 0 开始。
依次调用 mysql_fetch_row() 将返回结果集中的下一行，如果没有更多行则返回 FALSE。
-----------
下面函数主要用来处理数据格式,每次只能出来一条数据来进行格式化
 */
	public function fetchResult() {
		if ($row = mysql_fetch_row($this->results)) {
			$resultRow = array();
			$i = 0;
			foreach ($row as $index => $field) {
				list($table, $column) = $this->map[$index];
				$resultRow[$table][$column] = $row[$index];
				
				$i++;
			}
			return $resultRow;
		} else {
			return false;
		}
	}

/**
mysql_client_encoding() 函数返回当前连接的字符集的名称。
比如输出：latin1
 */
	public function getEncoding() {
		return mysql_client_encoding($this->connection);
	}
	
	public function setEncoding($enc) {
		return $this->_execute('SET NAMES ' . $enc) != false;
	}
/**
 * Query charset by collation
 *
 * @param string $name Collation name
 * @return string Character set name
 */
	public function getCharsetName($name) {
		if ((bool)version_compare(mysql_get_server_info($this->connection), "5", ">=")) {
			$cols = $this->fetchAll('SELECT CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME= ' . $this->value($name) . ';');
			if (isset($cols[0]['COLLATIONS']['CHARACTER_SET_NAME'])) {
				return $cols[0]['COLLATIONS']['CHARACTER_SET_NAME'];
			}
		}
		return false;
	}	
}

?>